# -*- coding: utf-8 -*-

"""
通用的计算图谱diff的代码
"""

from __future__ import unicode_literals
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division

from typing import List, Set, AnyStr, Dict, Tuple

DEFAULT_IGNORE_KEYS = {"@id", "dateModified"}
DEFAULT_IGNORE_KEYS_LINK = {"@id", "dateModified", "in", "out"}


def key_diff(dict1, dict2, ignore):  # type: (Dict, Dict, Set[AnyStr]) -> Tuple[Set[AnyStr], Set[AnyStr], Set[AnyStr]]
    """
    计算两个字典（JSON）的key差异。其中相同的key不代表那个key的值一定有变化，返回的都是有变化的key
    :param dict1:
    :param dict2:
    :param ignore: 忽略的字段
    :return: dict2相对dict1：删除的key，增加的key，变化的key
    """
    set1 = set(dict1.keys()) - ignore
    set2 = set(dict2.keys()) - ignore
    key_remove = set2 - set1
    key_add = set1 - set2
    key_same = set1 & set2
    key_update = set()
    for key in key_same:
        if dict1[key] != dict2[key]:
            key_update.add(key)
    return key_remove, key_add, key_update


def diff_handler_fix(dict1, dict2, ignore):  # type: (Dict, Dict, Set[AnyStr]) -> List[Dict]
    """
    Fix格式的diff handler。可以作为样例参考
    :param dict1:
    :param dict2:
    :param ignore:
    :return: dict
    """
    if dict1 is None:
        return [{"@id": dict2["@id"]}]
    elif dict2 is None:
        return [dict1]
    else:
        key_remove, key_add, key_update = key_diff(dict1, dict2, ignore)
        if key_remove or key_add or key_update:  # if there is any change
            res = {"@id": dict1["@id"]}
            for key in key_remove:
                res[key] = None
            for key in key_add:
                res[key] = dict1[key]
            for key in key_update:
                res[key] = dict1[key]
            return [res]
        else:
            return []


def is_dict_same(dict1, dict2, ignore):  # type: (Dict, Dict, Set[AnyStr]) -> bool
    """
    两个字典是否一样（考虑忽略字段），理论上比key_diff要快一些
    """
    if dict2 is None:
        return False
    set1 = set(dict1.keys()) - ignore
    set2 = set(dict2.keys()) - ignore
    if set1 != set2:
        return False
    for key in set1:
        if dict1[key] != dict2[key]:
            return False
    return True


def diff_handler_es(dict1, dict2, ignore):  # type: (Dict, Dict, Set[AnyStr]) -> [Dict]
    """
    ES发版用的diff handler。相对于Fix格式，不保留差异变更，只要有变化都当做新增
    """
    if dict1 is None:
        return [{"@id": dict2["@id"]}]
    elif is_dict_same(dict1, dict2, ignore):
        return []
    else:
        return [dict1]


def diff_handler_neo4j(dict1, dict2, ignore):  # type: (Dict, Dict, Set[AnyStr]) -> [Dict]
    """
    Neo4j发版用的diff handler。相对于Fix格式，@id字段更名为kgid，处理链接时，需要保留in、out字段，并且添加diff类型字段@diff
    """
    if dict1 is None:
        res = {"kgid": dict2["@id"], "@diff": "delete"}
        if "in" in dict2:
            res["in"] = dict2["in"]
        if "out" in dict2:
            res["out"] = dict2["out"]
        return [res]
    elif dict2 is None:
        res = dict1
        res["kgid"] = dict1["@id"]
        del res["@id"]
        res["@diff"] = "add"
        return [res]
    else:
        key_remove, key_add, key_update = key_diff(dict1, dict2, ignore)
        if key_remove or key_add or key_update:  # if there is any change
            res = {"kgid": dict1["@id"], "@diff": "update"}
            for key in key_remove:
                res[key] = None
            for key in key_add:
                res[key] = dict1[key]
            for key in key_update:
                res[key] = dict1[key]
            # for links, there are no change that its @id did not change yet any of `in` `out` `@type` changed
            if "CnsLink" in dict2["@type"]:
                res["in"] = dict2["in"]
                res["out"] = dict2["out"]
                res["@type"] = dict2["@type"]
            return [res]
        else:
            return []
